package com.zhouchao.dubbo.filter;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.dubbo.common.extension.SPI;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: zhouchao
 * @Date: 2021/09/19 19:34
 * @Description:
 */
@SPI("monitorTime")
public class MonitorTimeFilter implements TPMonitorFilter {

    Object lock = new Object();

    @Override
    public void serviceReport(String serviceName, Long time) {
        Cache<Long, Long> serviceCache = getCache(serviceName);

        Long now = System.currentTimeMillis();
        serviceCache.put(now, time);

    }

    @Override
    public Long getTP99(String serviceName) {
        return getTpTime(serviceName, 0.99);
    }

    @Override
    public Long getTP90(String serviceName) {
        return getTpTime(serviceName, 0.90);
    }

    private Cache<Long, Long> getCache(String serviceName) {
        Cache<Long, Long> serviceCache = monitor.get(serviceName);
        if (serviceCache == null) {
            synchronized (lock) {
                serviceCache = monitor.get(serviceName);
                if (serviceCache == null) {
                    serviceCache = CacheBuilder.newBuilder()
                            .expireAfterAccess(1, TimeUnit.MINUTES)
                            .maximumSize(Integer.MAX_VALUE)
                            .build();
                }
            }
        }
        monitor.put(serviceName, serviceCache);
        return serviceCache;
    }

    private Long getTpTime(String serviceName, double tp) {
        Cache<Long, Long> serviceCache = getCache(serviceName);
        ConcurrentMap<Long, Long> map = serviceCache.asMap();
        Collection<Long> values = map.values();
        if (CollectionUtils.isEmpty(values)) {
            return 0l;
        }

        List<Long> list = new ArrayList<>();
        list.addAll(values);
        list = values.stream().sorted().collect(Collectors.toList());

        // 计算index
        BigDecimal bigDecimal = new BigDecimal(list.size());
        int index = bigDecimal.multiply(new BigDecimal(tp)).intValue();

        if (CollectionUtils.isEmpty(list)) {
            return 0l;
        }

        return list.get(index);
    }
}
